#include "stdlib.hpp"
#include "libfile.hpp"

namespace purewell
{

#if !defined(__PW_HAVE_GETTIMEOFDAY) && defined(WIN32)

int
gettimeofday(struct timeval* tv, void*)
{
	if ( tv )
	{
		FILETIME ft;
		GetSystemTimeAsFileTime(&ft);

		uint64_t tmp(0);
		tmp |= ft.dwHighDateTime;
		tmp <<= 32;
		tmp |= ft.dwLowDateTime;

		tmp /= 10;
		tmp -= 11644473600000000ULL;

		tv->tv_sec = tmp / 1000000UL;
		tv->tv_usec = tmp % 1000000UL;
	}

	return 0;
}

#endif//!__PW_HAVE_GETTIMEOFDAY && __PW_HAVE_GETSYSTEMTIMEASFILETIME

#ifdef WIN32
int
__PWGetFDFromHandle(const HANDLE hdl, int flags)
{
	return _open_osfhandle((intptr_t)hdl, flags);
}

HANDLE
__PWGetHandleFromFD(int fd)
{
	return (HANDLE)_get_osfhandle(fd);
}

#endif//WIN32

uint64_t
__PWToMSecond(const struct timeval& tv)
{
	uint64_t res(tv.tv_sec * 1000ULL);
	res += tv.tv_usec;
	return res;
}

uint64_t
__PWGetNow(void)
{
	struct timeval tv;
	gettimeofday(&tv, NULL);

	uint64_t res(tv.tv_sec * 1000ULL);
	res += tv.tv_usec;
	return res;
}

#ifndef WIN32
bool
__PWCreateSharedInstance(__pw_shinst_t& out, size_t objsize)
{
	if ( objsize == 0 )
	{
		return false;
	}

	int shmfd(-1);
	ssize_t res;
	char tmpbuf[1024];
	void* pmap(MAP_FAILED);

	do
	{
		memset(tmpbuf, 0x00, sizeof(tmpbuf));

		shmfd = __PWCreateUnnamedSharedMemoryTemp();
		if ( -1 == shmfd )
		{
			break;
		}

		size_t leftsize(objsize);
		size_t cpsize(0);
		while ( leftsize > 0 )
		{
			cpsize = std::min(leftsize, sizeof(tmpbuf));
			res = write(shmfd, tmpbuf, cpsize);
			if ( res == -1 )
			{
				goto ERROR;
			}

			leftsize -= res;
		}

		pmap = mmap(NULL, objsize, PROT_READ|PROT_WRITE,
			MAP_SHARED, shmfd, 0);
		if ( pmap == MAP_FAILED )
		{
			break;	
		}

		out.p = pmap;
		out.size = objsize;
		out.fd = shmfd;

		return true;
	} while (false);

ERROR:
	if ( pmap != MAP_FAILED )
	{
		munmap(pmap, objsize);
		pmap = MAP_FAILED;
	}

	if ( shmfd != -1 )
	{
		close(shmfd);
		shmfd = -1;
	}

	return false;
}

void
__PWDestroySharedInstance(__pw_shinst_t& in)
{
	munmap(in.p, in.size);
	close(in.fd);

	in.p = NULL;
	in.size = 0;
	in.fd = -1;
}

int
__PWCreateUnnamedSharedMemoryTemp(void)
{
	int shmfd(-1);
	char tmpfn[1024];
	pid_t pid(getpid());

	do
	{
		snprintf(tmpfn, sizeof(tmpfn), "/.%d_tmp_%ju", pid, uintmax_t(rand()));

		errno = 0;
		shmfd = shm_open(tmpfn, O_RDWR|O_CREAT|O_EXCL, 0400);
		if ( -1 == shmfd )
		{
			if ( errno != EEXIST )
			{
				break;
			}
		}
		else
		{
			//gLibLog.format("Unnamed shared memory temp-file: " "fd: %d name: %s", shmfd, tmpfn);
			shm_unlink(tmpfn);
			break;
		}
	} while (true);

	return shmfd;
}
#endif//!WIN32

#ifndef WIN32
struct timespec&
__PWDiffTimespec(struct timespec& res,
	const struct timespec& ts1, const struct timespec& ts2)
{
	// 1 sec == 1,000,000,000 nanosec
	res.tv_sec = ts1.tv_sec - ts2.tv_sec;
	if ( ts1.tv_nsec < ts2.tv_nsec )
	{
		--res.tv_sec;
		res.tv_nsec = (1000000000LL - ts2.tv_nsec) + ts1.tv_nsec;
	}
	else
	{
		res.tv_nsec = ts1.tv_nsec - ts2.tv_nsec;
	}

	return res;
}
#endif//!WIN32

};//namespace purewell

